package com.sysd.mail.smtp;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import com.sysd.mail.config.ServerConfig;
import com.sysd.mail.entity.bean.EmailBox;
import com.sysd.mail.entity.bean.User;
import com.sysd.mail.exception.InvalidCommandNameException;
import com.sysd.mail.exception.UnKnowCommandException;
import com.sysd.mail.mapper.EmailBoxMapper;
import com.sysd.mail.mapper.UserMapper;
import com.sysd.mail.smtp.command.SmtpCommand;
import com.sysd.mail.util.*;
import org.xbill.DNS.MXRecord;

import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.io.IOException;
import java.util.*;

/**
 * project: mail
 *
 * @author bsh
 * @description SmtpCommandHandler
 * @dataTime 2022/12/28 10:54
 * @company 丽水信阳软件科技有限公司
 */
public class SmtpCommandHandler {
    private static final Map<String, SmtpCommand> COMMAND_MAP = new HashMap<>();
    private final EmailBoxMapper emailBoxMapper = SpringContextHolder.getBean("emailBoxMapper");
    private final UserMapper userMapper = SpringContextHolder.getBean("userMapper");

    SmtpCommandHandler() {
        for (SmtpCommandFactory smtpCommandRegistry : SmtpCommandFactory.values()) {
            addCommand(smtpCommandRegistry.getCommand());
        }
    }

    public void addCommand(SmtpCommand smtpCommand) {
        COMMAND_MAP.put(smtpCommand.getCommand(), smtpCommand);
    }

    /**
     * 处理命令
     *
     * @param smtpSession   session
     * @param commandString command str
     * @return smtp session
     */
    public SmtpSession handleCommand(SmtpSession smtpSession, String commandString) {
        if (smtpSession.isMailTransactionInProcess() && smtpSession.isDataProcess()) {
            return this.saveData(smtpSession, commandString);
        }
        if(smtpSession.isLoginProcess()){
            int loginProcess = smtpSession.getLoginProcessCount();
            if (loginProcess == 1) {
                return this.verifyUserName(smtpSession, commandString);
            } else if (loginProcess == 2) {
                return this.verifyPassword(smtpSession, commandString);
            }
        }
        try {
            SmtpCommand command = this.getCommand(commandString);
            command.execute(commandString, smtpSession);
        } catch (IOException e) {
            smtpSession.write("500 " + e.getMessage() + "\r\n");
        }
        return smtpSession;
    }

    private SmtpSession saveData(SmtpSession smtpSession, String commandString) {
        if (".".equals(commandString)) {
            smtpSession.write("250 Requested mail action okay, completed!\r\n");
            String inboxContent = smtpSession.getBuf().toString();
            for (String item : smtpSession.getEmail().getTo()) {
                EmailBox emailBox = EmailBox.builder()
                        .emailBoxDate(DateUtil.getCurrentDT())
                        .emailBoxFrom(smtpSession.getEmail().getFrom())
                        .emailBoxTo(item)
                        .emailBoxStr(inboxContent)
                        .emailBoxSubject(smtpSession.getEmail().getSubject())
                        .flag(0)
                        .build();
                if(EmailUtils.getDomain(emailBox.getEmailBoxTo()).equals(ServerConfig.DOMAIN)){
                    emailBoxMapper.insert(emailBox);
                }
                else {
                        MXRecord[] mxRecords= DNSUtil.getMxRecords(EmailUtils.getDomain(item));
                        for (MXRecord mxRecord : mxRecords) {
                            Properties properties=new Properties();
                            properties.setProperty("mail.transport.protocol", "smtp");
                            properties.setProperty("mail.host", mxRecord.getTarget().toString());
                            properties.setProperty("mail.user", smtpSession.getEmail().getFrom());
                            properties.setProperty("mail.from", smtpSession.getEmail().getFrom());
                            Session session=Session.getInstance(properties);
                            session.setDebug(true);
                            MimeMessage message=new MimeMessage(session);
                            try {
                                String formName = MimeUtility.encodeWord(smtpSession.getEmail().getFrom()) + " <" + smtpSession.getEmail().getFrom() + ">";
                                InternetAddress from = new InternetAddress(formName);
                                message.setFrom(from);
                                InternetAddress to = new InternetAddress(smtpSession.getEmail().getTo().get(0));
                                message.setRecipient(Message.RecipientType.TO, to);
                                message.setSubject(smtpSession.getEmail().getSubject());
                                message.setContent(smtpSession.getBuf().toString(),"text/html;charset=UTF-8");
                                message.saveChanges();
                                Transport transport=session.getTransport();
                                transport.connect();
                                transport.sendMessage(message, message.getAllRecipients());
                                break;
                            }
                            catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                }
            }
            smtpSession.resetMailTransaction();
            return smtpSession;
        } else if (commandString.startsWith(".")) {
            smtpSession.setBuf(println(smtpSession.getBuf(), commandString.substring(1)));
            return smtpSession;
        } else {
            smtpSession.setBuf(println(smtpSession.getBuf(), commandString));
        }
        return smtpSession;
    }

    /**
     * 写入内容
     *
     * @param buf  str
     * @param line 每一行
     * @return string builder
     */
    private StringBuilder println(StringBuilder buf, String line) {
        buf.append(line).append("\r\n");
        return buf;
    }

    public SmtpSession verifyUserName(SmtpSession smtpSession, String commandString) {
        byte[] decoded = Base64.decode(commandString);
        if (decoded == null) {
            smtpSession.write("501 Syntax error in parameters or arguments\r\n");
            return smtpSession;
        }
        smtpSession.getUser().setEmailAddress(TextUtils.getStringUtf8(decoded));
        smtpSession.write("334 UGFzc3dvcmQ6\r\n");
        smtpSession.setLoginProcessCount(2);
        return smtpSession;
    }

    public SmtpSession verifyPassword(SmtpSession smtpSession, String commandString) {
        byte[] decoded = Base64.decode(commandString);
        if (decoded == null) {
            smtpSession.write(
                   "501 \r\n");
            return smtpSession;
        }
        String password = TextUtils.getStringUtf8(decoded);
        User user = userMapper.selectOne(new QueryWrapper<User>()
                .eq("email_address", smtpSession.getUser().getEmailAddress())
                .eq("password", password));
        if (user == null) {
            smtpSession.setUser(new User());
            smtpSession.write("535 " +
                    "r\n");
            smtpSession.setLoginProcessCount(0);
            smtpSession.setLogin(false);
            return smtpSession;
        } else {
            System.out.println("登陆成功");
            smtpSession.write("235 \r\n");
            smtpSession.setLogin(true);
            smtpSession.setUser(user);
            smtpSession.setLoginProcessCount(0);
            return smtpSession;
        }
    }

    /**
     * 获取命令的具体实现
     *
     * @param commandString 命令字符串
     * @return smtp command
     */
    public SmtpCommand getCommand(String commandString) {
        SmtpCommand smtpCommand = null;
        String key = toKey(commandString);
        smtpCommand = COMMAND_MAP.get(key);
        // 超过四个字符的命令
        if (smtpCommand == null) {
            // some commands have a verb longer than 4 letters
            String verb = toVerb(commandString);
            smtpCommand = COMMAND_MAP.get(verb);
        }
        // 未知的命令
        if (smtpCommand == null) {
            throw new UnKnowCommandException("502 Command not implemented!\r\n");
        }
        return smtpCommand;
    }

    /**
     * 格式化命令
     *
     * @param commandString 命令字符串
     * @return command str
     * @throws InvalidCommandNameException 无效的命令
     */
    private static String toKey(String commandString) throws InvalidCommandNameException {
        if (commandString == null || commandString.length() < 4) {
            throw new InvalidCommandNameException("500 Syntax error, command unrecognized!\r\n");
        }
        return commandString.substring(0, 4).toUpperCase(Locale.ENGLISH);
    }

    /**
     * 匹配自定义的命令
     *
     * @param commandString command str
     * @return command
     * @throws InvalidCommandNameException 无效的命令
     */
    private static String toVerb(String commandString) throws InvalidCommandNameException {
        StringTokenizer stringTokenizer = new StringTokenizer(commandString);
        if (!stringTokenizer.hasMoreTokens()) {
            throw new InvalidCommandNameException("500 Syntax error, command unrecognized!\r\n");
        }
        return stringTokenizer.nextToken().toUpperCase(Locale.ENGLISH);
    }
}
